//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.CodeDom;
using System.Diagnostics.Contracts;
using System.Text;

namespace OpenLS.Spreadsheet.Parsing
{
    /// <summary>
    /// Summary description for Lexer.
    /// </summary>
    sealed class Lexer: LexerCreators
    {
        public Lexer()
        {
            Build();
        }

        public void SetAt(Token t)
        {
            Position = t.Position;
        }
        public void SetInput(string text)
        {
            Column = 0;
            Line = 1;
            Position = 0;
            _chars = new char[text.Length + 1];
            Array.Copy(text.ToCharArray(), 0, _chars, 0, text.Length);
            _chars[text.Length] = '\0';
        }
        public string Substring(int start, int len)
        {
            return new String(_chars, start, len);
        }
        protected CodeLinePragma CurrentPragma
        {
            get
            {
                return new CodeLinePragma("no file", Column + 1);
            }
        }

        private Token _previousToken;
        private Token _afterPrevious;
        public Token NextToken()
        {
            Token result;
            if (_afterPrevious != null)
            {
                _previousToken = _afterPrevious;
                result = _afterPrevious;
                _afterPrevious = null;
                return result;
            }
            result =  nextTokenImp();
            if (addSpace(_previousToken, result))
            {
                _afterPrevious = result;
                result = new SpaceToken();
                return result;

            }
            _previousToken = result;
            return result;
        }

        static bool addSpace (Token token1, Token token2)
        {
            if (token1 == null)
                return false;
            if (token2 == null)
                return false;
            switch (token1.Type)
            {
                case TokenId.Identifier:
                    case TokenId.Numeric:
                    switch (token2.Type)
                    {
                        case TokenId.Identifier:
                            case TokenId.Numeric:
                            case TokenId.Dollar:
                            {
                int lastPrevious = token1.Position + token1.Length;
                int thisPosition = token2.Position;
                                return (lastPrevious < thisPosition);
                                
                            }
                    }
                    break;
            }
            return false;
        }

        private Token nextTokenImp()
        {
            if (Position >= _chars.Length)
                return null;
            char c = this[Position];
            if (c == '\0')
            {
                return null;
            }
            int charIndex = c;
            if (charIndex > _creators.Length)
            {
                if (char.IsLetter(c))
                    charIndex = 'a';
                else
                    switch (charIndex)
                    {
                        default:
                            charIndex = 'a'; break;//throw new NotSupportedException();
                    }
            }
            TokenCreator[] lcreators = _creators[charIndex];
            if (lcreators == null)
            {
                if (char.IsLetter(c) || c == '#' || true)
                    lcreators = _creators[(int)'a'];
                else
                    throw new InvalidCharacterException(c);
            }
            int count = lcreators.Length;
            for (int i = 0; i < count; i++)
            //foreach (TokenCreator creator in lcreators)
            {
                TokenCreator creator = lcreators[i];
                Token t = creator.Match(this);
                if (creator is SpaceProcessor)
                {
                    Position = Position + creator.MatchSize();
                    return NextToken();
                }
                if (t != null)
                {
                    t.Position = Position;
                    int ccol = Column;
                    Column += creator.MatchSize();
                    t.Length = Column - ccol;
                    Position = Position + creator.MatchSize();
                    return t;
                }
            }
            Position = _chars.Length;
            return null;
            //\\return new ExplicitString("to implement", "to implement");
        }
        public int Line;
        public int Column;
        public int Position;
        char[] _chars;
        public char this[int i]
        {
            get
            {
                return _chars[i];
            }
        }
        public char CurrentCharAt(int i)
        {
            Contract.Requires(i <= MaxIndex);
            return this[Position + i];
        }
        internal int MaxIndex{get{ return _chars.Length - 1 - Position;}}

        public string EndString()
        {
            StringBuilder sb = new StringBuilder();
            for (int i = Position; i < _chars.Length; i++)
            {
                if (_chars[i] != '\0')
                sb.Append(_chars[i]);
                
            }
            return sb.ToString();
        }
    }
}
